﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using Tools.Debugging;
using repliback4.Net.Protocol;
using repliback4.XmlStreaming;
using repliback4.Net.Protocol.States.ClientSide;
using System.Xml;
using repliback4.Net.Protocol.Messages;
using repliback4.Misc;


namespace repliback4.Net.ClientSide {
    public class ServerInfo {
        public Boolean Authorized;
        public Boolean Encrypted;
        public List<Stanza> Features = new List<Stanza>( );

        public void AddFeatures ( List<Stanza> list ) {
            this.Features.AddRange( list );
        }
        public void AddFeature ( Stanza feature ) {
            this.Features.Add( feature );
        }

        public List<Stanza> GetFeatures ( String featureName ) {
            return this.Features.FindAll( new Predicate<Stanza>(
                 ( s ) => ( s.TagName == featureName )
             ) );
        }
        public List<Stanza> GetFeatures ( Type type ) {
            return GetFeatures( type.GetReplibackTagName( ) );
        }
        public bool HasFeature ( string featureName ) {
            return this.GetFeatures( featureName ).Count >= 0;
        }

        public bool HasFeature ( Type type ) {
            return this.HasFeature( type.GetReplibackTagName( ) );
        }
    }

    public class Server : SideEndpoint {
        public ServerInfo ServerInfo;
        public Client Client;
        public ClientConnection connection;
        public Server ( Client client, TcpClient connection ) {

            Logger.Created( this );

            this.Client = client;

            this.ServerInfo = new ServerInfo( );

            Logger.Debug( this, "connection callbacks binding" );
            this.connection = new ClientConnection( connection );
            this.connection.OnConnect += new AsyncConnection.OnConnectHandler( connection_OnConnect );
            this.connection.OnDisconnect += new AsyncConnection.OnDisconnectHandler( connection_OnDisconnect );
            this.connection.OnReceive += new AsyncConnection.OnReceiveHandler( connection_OnReceive );
            this.connection.OnRemoteDisconnect += new AsyncConnection.OnRemoteDisconnectHandler( connection_OnRemoteDisconnect );


            Logger.Debug( this, "creating message parser" );
            this.parser = new MessageParser( );

            Logger.Debug( this, "parser callbacks binding" );
            this.parser.OnStreamBegin += new MessageParser.OnStreamBeginHandler( parser_OnStreamBegin );
            this.parser.OnStreamEnd += new MessageParser.OnStreamEndHandler( parser_OnStreamEnd );
            this.parser.OnStreamError += new MessageParser.OnStreamErrorHandler( parser_OnStreamError );
            this.parser.OnTag += new MessageParser.OnTagHandler( parser_OnTag );
            this.parser.OnXmlDeclaration += new MessageParser.OnXmlDeclarationHandler( parser_OnXmlDeclaration );

            this.writer = new XmlStreamWriter( );


            Logger.Debug( this, "creating state manager" );
            this.stateManager = new ClientStateManager( this );
            this.stateManager.CurrentState = new DisconnectedState( this.stateManager );

            this.connection.Start( );
        }
        /*
        void parser_OnXmlDeclaration ( System.Xml.XmlNode xmlDeclaration ) {
            throw new NotImplementedException( );
        }

        void parser_OnTag ( repliback4.Net.Protocol.Messages.ReplibackTag stanza ) {
            throw new NotImplementedException( );
        }

        void parser_OnStreamError ( ) {
            throw new NotImplementedException( );
        }

        void parser_OnStreamEnd ( repliback4.Net.Protocol.Messages.BaseStream stream ) {
            throw new NotImplementedException( );
        }

        void parser_OnStreamBegin ( repliback4.Net.Protocol.Messages.BaseStream stream ) {
            throw new NotImplementedException( );
        }*/

        //public void 

        void connection_OnRemoteDisconnect ( RemoteDisconnectData data ) {
            Logger.Debug( this, "remote host disconnected" );
        }

        void connection_OnConnect ( ConnectData data ) {
            Logger.Debug( this, "firing connection_OnConnect... binding XmlStreamWriter to socket" );
            this.writer.Writer = data.connection.StreamWriter;
            this.stateManager.CurrentState.Execute( );

        }

        void connection_OnDisconnect ( DisconnectData data ) {
        }

        void connection_OnReceive ( Response response ) {
            Logger.Debug( this, "received: {0}", response.Data );
            try {
                this.parser.Parse( response.Data );
            } catch ( XmlException e ) {
                Logger.Exception( this, "connection_OnReceive: XmlException", e );
            } catch ( Exception e ) {
                Logger.Exception( this, "connection_OnReceive: Exception", e );
            }
        }

        internal void Send ( string message ) {
            this.connection.Send( message );
        }
    }
}
